#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

// 用户运行的每一个进程都有其父进程
// 当进程处于调度队列时，此时就表示该进程处于R
// 不一定是要被cpu执行才叫做运行，只要处于调度队列

void testRunning()
{
    while(1)
    {
        int a = 0;
        a++;
    }
}

// S状态对应操作系统中的阻塞状态
// 比如当程序需要访问硬件设备时，此时就会处于一种阻塞状态
// 例如我们在程序中使用scanf/cin这样的函数
// 当程序运行到这些语句时，此时进程就会进入S状态
// 直到我们按下键盘
// 本质上，每一个硬件设备都有其自己的描述
// 当进程进入阻塞状态时，其实就是这个进程链到了该设备描述中的waitqueue

void testSleeping()
{
    while(1)
    {
        int x = 0;
        scanf("%d",&x);
    }
}

// 上面的S状态其实是一种可中断睡眠/浅睡眠
// 当进程处于s状态时，我们可以使用ctrl+c/kill 来终止进程
// 在linux中还有一种阻塞状态：D(disk sleep)
// 它是一种不可中断睡眠/深睡眠
// 该状态一般出现在，高IO的场景—对磁盘的大规模读或者写
// 因为对磁盘进行读或者写操作可能会出现错误
// 所以需要等待磁盘的返回信息，为了在等待时不让该进程被杀掉，所以需要用D状态来标记

void testDiskSleep()
{
    // 高IO操作
    // 可以尝试给磁盘写入大量数据
    // 但可能导致磁盘空间不足
}

// linux中还有两个操作系统中没有描述的状态T(stopped)、t(tracing stop)
// 两者表示的是暂停
// t：程序被debug时，遇到断点时，程序会停下来，这就是暂停t
// 我们可以使用ctrl+z使程序暂停，此时进程就处于T状态
// 这其实是我们发送的信号
// kill -18 可以使进程继续  kill -19 可以进程暂停
void testTt()
{
    int i = 0;
    while(1)
    {
        printf("我是一个进程，我的pid是:%d\n",getpid());
    }
}

// X(dead)和Z(zombie)
// X就表示结束状态，即进程执行结束
// 但因为进程一结束，操作系统就会立即收回资源，所以我们观察不到X这个状态
// Z表示僵尸进程，当一个子进程异常结束之后，操作系统不会立即释放资源
// 而是会返回子进程的结束信息给父进程，如果父进程一直不接收，那么子进程就会一直处于僵尸进程状态

void testxz()
{
    // 创建子进程
    pid_t id = fork();
    if(id == 0)
    {
        // child
        // 啥也不干，让子进程结束
        printf("我是子进程\n");
    }
    else
    {
        while(1)
        {
            printf("我是父进程\n");
        }
    }
}

// 子进程一直不结束，就有可能导致内存泄漏
// 内存泄漏有可能由new，malloc，僵尸进程导致
// 进程结束后，内存泄露就不存在了，操作系统会在进程结束后，自动回收申请的资源
// 我们练习时写的程序不必担心内存泄露的问题，因为程序一运行一下就结束了。
// 内存泄露一般只存在于那些一直在处于运行状态的进程/常驻内存中的进程

// 挂起
// 在宏观的操作系统上，还有挂起这一状态，挂起分为运行挂起和阻塞挂起
// 当内存容量不够时，而此时还不断有新的进程等待运行，此时操作系统会首先查看那些被阻塞的进程
// 将这些进程的数据和代码唤出->放到磁盘的某个位置，等到该进程被响应之后，操作系统再将该进程的代码和数据唤入，此时该进程又重新链接到调度队列中
// 运行挂起则是更极端的场景，将阻塞状态的进程都挂起之后，内存还是不够，此时就会将调度队列靠后的几个进程的代码和数据唤出，等到被调度时，再将其唤入

int main()
{
    testxz();
    // testTt();
    //testTtRunning();
    // testSleeping();
    return 0;
}
